package com.tc.vms.message;

import com.tc.vms.exception.VmsClientException;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by yonghua.zhang on 2015/12/12.
 * client generate uniq message id
 * ip:4byte, port:2byte, processId:2byte, seq:8byte
 */
public class MessageId {
    private final static AtomicLong seqGenerator = new AtomicLong(0L);
    private SocketAddress address;
    private int processId;
    private long msgSeq;

    public MessageId(SocketAddress address, int processId, long msgSeq) {
        this.address = address;
        this.processId = processId;
        this.msgSeq = msgSeq;
    }

    public MessageId(SocketAddress address, int processId) {
        this(address, processId, seqGenerator.incrementAndGet());
    }

    public long getMsgSeq() {
        return msgSeq;
    }

    public SocketAddress getAddress() {
        return address;
    }

    public void setAddress(SocketAddress address) {
        this.address = address;
    }

    public int getProcessId() {
        return processId;
    }

    public void setProcessId(int processId) {
        this.processId = processId;
    }

    public String encode() {
        ByteBuffer byteBuffer = ByteBuffer.allocate(4 + 2 + 2 + 8);
        InetSocketAddress inetSocketAddress = (InetSocketAddress) address;

        if (!inetSocketAddress.isUnresolved()) {
            byteBuffer.put(inetSocketAddress.getAddress().getAddress());
        } else {
            byteBuffer.put(new byte[4]);
        }

        byteBuffer.putShort((short) inetSocketAddress.getPort());
        byteBuffer.putShort((short) processId);
        byteBuffer.putLong(msgSeq);
        byteBuffer.flip();
        return Long.toHexString(byteBuffer.getLong()) + "-" + Long.toHexString(byteBuffer.getLong());
    }

    public static MessageId decode(String messageId) throws VmsClientException {
        if (messageId == null || messageId.isEmpty()) return null;

        int pid = 0;
        long msgSeq = 0L;
        InetSocketAddress isa = null;
        try {
            String[] parts = messageId.split("-");
            if (parts.length != 2) return null;
            Long prefix = null;
            String prefixStr = parts[0].trim();
            try {
                prefix = Long.parseLong(prefixStr, 16);
            } catch (NumberFormatException e) { //first byte > 127
                long test = Long.parseLong(prefixStr.substring(0, 2), 16);
                prefix = Long.parseLong(prefixStr.substring(2), 16);
                prefix |= (test << 56);
            }
            Long suffix = null;
            String suffixStr = parts[1].trim();
            try {
                suffix = Long.parseLong(suffixStr, 16);
            } catch (NumberFormatException e) { //first byte > 127
                long test = Long.parseLong(suffixStr.substring(0, 2), 16);
                suffix = Long.parseLong(suffixStr.substring(2), 16);
                suffix |= (test << 56);
            }
            ByteBuffer byteBuffer = ByteBuffer.allocate(4 + 2 + 2 + 8);
            byteBuffer.putLong(prefix);
            byteBuffer.putLong(suffix);
            byteBuffer.flip();

            byte[] addrBytes = new byte[4];
            int port = 0;
            byteBuffer.get(addrBytes);
            port = byteBuffer.getShort() & 0xFFFF;
            pid = byteBuffer.getShort() & 0xFFFF;
            msgSeq = byteBuffer.getLong();
            isa = new InetSocketAddress(InetAddress.getByAddress(addrBytes), port);
        } catch (NumberFormatException e) {
            throw new VmsClientException("Illegal format of messageid:" + messageId, e);
        } catch (UnknownHostException e) {
            throw new VmsClientException("Illegal HostIp by decoding messageid:" + messageId, e);
        }
        return new MessageId(isa, pid, msgSeq);
    }

    @Override
    public String toString() {
        return "MessageId{" +
                "address=" + address +
                ", processId=" + processId +
                ", msgSeq=" + msgSeq +
                '}';
    }
}
